using System;
using System.Net.Mail;
using System.Collections.Generic;

using BlogEngine.Core.Providers;

namespace BlogEngine.Core
{
	/// <summary>
	/// Represents a comment to a blog post.
	/// </summary>
	[Serializable]
	public class Comment : BusinessBase<Comment, int>, IPublishable, IComparable<Comment>
	{
		private Post _post;
		private CommentType _type;
		private CommentStatus _status;
		private string _email;
		private Uri _website;
		private string _ip;
		private string _agent;
		private string _author;
		private DateTime? _datePublished;
		private string _content;

		#region Events

		/// <summary>
		/// Occurs when the post is being served to the output stream.
		/// </summary>
		public static event EventHandler<ServingEventArgs> Serving;
		/// <summary>
		/// Raises the event in a safe way
		/// </summary>
		public static void OnServing(Comment comment, ServingEventArgs arg)
		{
			if (Serving != null)
			{
				Serving(comment, arg);
			}
		}

		/// <summary>
		/// Raises the Serving event
		/// </summary>
		public void OnServing(ServingEventArgs arg)
		{
			if (Serving != null)
			{
				Serving(this, arg);
			}
		}

		/// <summary>
		/// Occurs when the page is being attacked by robot spam.
		/// </summary>
		public static event EventHandler<EventArgs> SpamAttack;

		/// <summary>
		/// Raises the SpamAttack event in a safe way
		/// </summary>
		public static void OnSpamAttack()
		{
			if (SpamAttack != null)
			{
				SpamAttack(null, new EventArgs());
			}
		}

		/// <summary>
		/// Occurs just before a comment is approved by the comment moderator.
		/// </summary>
		public static event EventHandler<EventArgs> Approving;

		/// <summary>
		/// Raises the event in a safe way
		/// </summary>
		internal static void OnApproving(Comment comment)
		{
			if (Approving != null)
			{
				Approving(comment, EventArgs.Empty);
			}
		}

		/// <summary>
		/// Occurs after a comment is approved by the comment moderator.
		/// </summary>
		public static event EventHandler<EventArgs> Approved;

		/// <summary>
		/// Raises the event in a safe way
		/// </summary>
		internal static void OnApproved(Comment comment)
		{
			if (Approved != null)
			{
				Approved(comment, EventArgs.Empty);
			}
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the post.
		/// </summary>
		/// <value>The post.</value>
		public Post Post
		{
			get { return _post; }
			set
			{
				if (_post != value)
					OnPropertyChanged("Post");

				_post = value;
			}
		}

		public CommentType Type
		{
			get { return _type; }
			set
			{
				if (_type != value)
					OnPropertyChanged("Type");

				_type = value;
			}
		}

		/// <summary>
		/// Gets or sets the status.
		/// </summary>
		/// <value>The status.</value>
		public CommentStatus Status
		{
			get { return _status; }
			set
			{
				if (_status != value)
					OnPropertyChanged("Status");

				_status = value;
			}
		}

		/// <summary>
		/// Gets or sets the email.
		/// </summary>
		/// <value>The email.</value>
		public string Email
		{
			get { return _email; }
			set
			{
				if (_email != value)
					OnPropertyChanged("Email");

				_email = value;
			}
		}

		/// <summary>
		/// Gets or sets the website.
		/// </summary>
		/// <value>The website.</value>
		public Uri Website
		{
			get { return _website; }
			set
			{
				if (_website != value)
					OnPropertyChanged("Website");

				_website = value;
			}
		}

		/// <summary>
		/// Gets or sets the IP.
		/// </summary>
		/// <value>The IP.</value>
		public string IP
		{
			get { return _ip; }
			set
			{
				if (_ip != value)
					OnPropertyChanged("IP");

				_ip = value;
			}
		}

		/// <summary>
		/// Gets or sets the agent.
		/// </summary>
		/// <value>The agent.</value>
		public string Agent
		{
			get { return _agent; }
			set
			{
				if (_agent != value)
					OnPropertyChanged("Agent");

				_agent = value;
			}
		}

		#region Transition Properties

		public string Country
		{
			get { return "US"; }
		}

		public Post Parent
		{
			get { return Post; }
		}

		public bool IsApproved
		{
			get { return Status == CommentStatus.Approved; }
			set { Status = value ? CommentStatus.Approved : CommentStatus.Pending; }
		}

		#endregion

		#endregion

		#region Base overrides

		/// <summary>
		/// Validates the Post instance.
		/// </summary>
		protected override void Validate()
		{
			AddError(Post == null, "Post", "The post that comments belongs to must be set.");
			AddError(String.IsNullOrEmpty(Content), "Content", "Content must be set.");
		}

		/// <summary>
		/// Returns a Post based on the specified id.
		/// </summary>
		protected override Comment DataSelect(int id)
		{
			return BlogService.SelectComment(id);
		}

		/// <summary>
		/// Updates the Post.
		/// </summary>
		protected override void DataUpdate()
		{
			BlogService.UpdateComment(this);
		}

		/// <summary>
		/// Inserts a new post to the current BlogProvider.
		/// </summary>
		protected override void DataInsert()
		{
			BlogService.InsertComment(this);
		}

		/// <summary>
		/// Deletes the Post from the current BlogProvider.
		/// </summary>
		protected override void DataDelete()
		{
			BlogService.DeleteComment(this);
		}

		/// <summary>
		/// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
		/// </returns>
		public override string ToString()
		{
			return String.Format("{0:d} - {1}", DateCreated, Author);
		}

		#endregion

		#region IPublishable Members

		/// <summary>
		/// Gets the author.
		/// </summary>
		/// <value>The author.</value>
		public string Author
		{
			get { return _author; }
			set
			{
				if (_author != value)
					OnPropertyChanged("Author");

				_author = value;
			}
		}

		/// <summary>
		/// Gets the perma link.
		/// </summary>
		/// <value>The perma link.</value>
		public Uri PermaLink
		{
			get { return new Uri(Post.PermaLink, "#comment-" + Id); }
		}

		/// <summary>
		/// Gets the date published.
		/// </summary>
		/// <value>The date published.</value>
		public DateTime? DatePublished
		{
			get
			{
				if (_datePublished == DateTime.MinValue)
					return _datePublished;

				return _datePublished;
			}
			set
			{
				if (_datePublished != value)
					OnPropertyChanged("DatePublished");

				_datePublished = value;
			}
		}

		/// <summary>
		/// Gets the content.
		/// </summary>
		/// <value>The content.</value>
		public string Content
		{
			get { return _content; }
			set
			{
				if (_content != value)
					OnPropertyChanged("Content");

				_content = value;
			}
		}

		/// <summary>
		/// Gets a value indicating whether this instance is visible.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is visible; otherwise, <c>false</c>.
		/// </value>
		public bool IsVisible
		{
			get { return Status == CommentStatus.Approved; }
		}

		/// <summary>
		/// Gets the title of the object
		/// </summary>
		/// <value></value>
		string IPublishable.Title { get { return String.Empty; } }

		/// <summary>
		/// Gets the description.
		/// </summary>
		/// <value>The description.</value>
		string IPublishable.Excerpt { get { return String.Empty; } }

		/// <summary>
		/// Gets the categories.
		/// </summary>
		/// <value>The categories.</value>
		IList<Term> IPublishable.Categories { get { return null; } }

		/// <summary>
		/// Gets the tags.
		/// </summary>
		/// <value>The tags.</value>
		IList<Term> IPublishable.Tags { get { return null; } }

		#endregion

		#region IComparable<Comment> Members

		/// <summary>
		/// Compares the current object with another object of the same type.
		/// </summary>
		/// <param name="other">An object to compare with this object.</param>
		/// <returns>
		/// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the <paramref name="other"/> parameter.Zero This object is equal to <paramref name="other"/>. Greater than zero This object is greater than <paramref name="other"/>.
		/// </returns>
		public int CompareTo(Comment other)
		{
			return DateTime.Compare(this.DateCreated, other.DateCreated);
		}

		#endregion
	}
}
